import { Singleton } from "./Common";

export default class utilTool extends Singleton<utilTool>{
    // 对象是否有效
    public isValid<T>(obj:T):boolean{
        if (obj === undefined || obj === null) return false;
        else if (typeof obj === 'number' && isNaN(obj)) return false;
        else if (typeof obj === 'string' && obj.length < 1) return false;
        return  true;
    };
    
    /**
     * 字符串是否为空
     * @param str 字符串
     * @param type 去空格标识
     * @returns 
     */
    public isEmpty(str: string, type: number = 0): boolean { 
        if (!str || typeof str !== 'string' || str.length < 1) {
            return true;
        }
        return this.trim(str).length < 1;
    }
    /**
     * 去掉字符串中空格
     * @param str 字符串
     * @param type 去空格标识
     * @returns 
     */
     public trim(str: string, type: number = 0): string{
        if (!str || typeof str !== 'string' || str.length < 1) {
            return '';
        }

        switch (type){
            case 0: return str.replace(/(^\s*)|(\s*$)/g, "");
            case 1: return str.replace(/(^\s*)/g, "");;
            case 2: return str.replace(/(\s*$)/g, "");;
        }
        return str;
    };

    public getNextOrBack<T>(arrays:Array<T>,item:T,isNext:boolean = true):T{
        if (!arrays || arrays.length < 1)
            return null;

        if (item === null)
            return arrays[0];
             
        if (!isNext) {
            let i = arrays.length-1;
            for (; i > 0; --i){
                if (arrays[i] === item) break ;
            }
            let index = i-1<0?arrays.length-1:i-1;
            return arrays[index % arrays.length]; 
        }

        let i = 0;
        for (i = 0; i < arrays.length; ++i) if (arrays[i] === item) break;
        return arrays[(i + 1) % arrays.length];
    };

    /**
     * 在保证原数组顺序的基础上乱序一个数组元素
     * @param list 
     * @returns 
     */
    public confusionArray(list:any[]):any[]{
        let tmp = list.slice(0);
        for(let i = list.length - 1; i >= 0; i--) {
            let randomIndex = Math.floor(Math.random() * (i+1));
            let itemtAtIndex = tmp[randomIndex];
            tmp[randomIndex] = tmp[i];
            tmp[i] = itemtAtIndex;
        }

        return tmp;
    }

    /**
     * 获取 fun 的执行时间。一般用来调试，评估优化使用
     * @param fun 函数指针
     */
    public executeTime(fun:Function):number{
        if (typeof(fun) === 'function'){
            let _begin:number = (new Date()).getTime();
            fun();
            let _end:number = (new Date()).getTime(); 
            let _time = _end - _begin;
            console.info('Execute function--[[[\n'+fun.toString()+'\n]]] consumes time:'+_time/1000+'s.');
            return _time;
        }
        return 0;
    };

    // 在source前面填充N个element使之长度为len
    public prefix(source:string|number,element:string,len:number):string{
        return (Array(len).join(element) + source).slice(-len);
    }

    public startWith(raw:string, str:string):boolean{
        return (new RegExp('^' + str)).test(raw);
    }

    public endWith(raw:string, str:string):boolean{
        return (new RegExp(str + '$')).test(raw);
    }

    public subUTF8Str(str:string, len?:number):string{
        let strLength = 0;
        let strLen = 0;
        let strCut = '';
        strLen = str.length;
        for (let i = 0; i < strLen; i++) {
            let a = str.charAt(i);
            strLength++;
            if (escape(a).length > 4) {
                //中文字符的长度经编码之后大于4  
                strLength++;
            }
            strCut = strCut.concat(a);
            if (strLength >= len) {
                strCut = strCut.concat("...");
                return strCut;
            }
        }
        //如果给定字符串小于指定长度，则返回源字符串；  
        if (strLength < len) {
            return str;
        }
        return "?";
    }

    public format(...args:any[]):string{
        let as = [].slice.call(arguments),
        fmt = as.shift(),i = 0;
        if (!this.isValid(fmt))
            return ;

        if (typeof fmt !== 'string') fmt = fmt.toString();
        return fmt.replace(/%(\w)?(\d)?([dfsx])/ig, (_, a, b, c)=>{
            let s = b ? new Array(b - 0 + 1).join(a || "") : "";
            if (c == "d") s += parseInt(as[i++]);
            if (c == "f") s += parseFloat(as[i++]);
            else if (c == "s") s += as[i++];
            return b ? s.slice(b * -1) : s;
        });
    };

    public formatStr(str:string, max:number, extra:string = '...'):string {
        if (str && str.length <= max) {
            return str;
        }
        let cnt = 0, len = 0;
        max = max * 2;
        for (let i = 0; i < str.length; i++) {
            len += str.charCodeAt(i) > 255 ? 2 : 1;
            cnt += 1;
            if (len > max) break;
        }
        if (len <= max) return str;
        return str.substr(0, cnt - 2) + extra;
    }
    
    public formatByte(val:number):string{
        if (isNaN(val)) return '';
        if (val < 1000) return ''+val;
        let _str = '';
        let _units = ['K', 'M', 'B'];
        let _dividend = 1000; 
        let curentUnit = _units[0]; //转换单位 
        let _strLen = (tempNum)=>{ 
            let _strNum = tempNum.toString(); 
            let _index = _strNum.indexOf("."); 
            let _newNum = _strNum; 
            if(_index!=-1) _newNum = _strNum.substring(0,_index); 
            return _newNum.length;
        }
    
        for (let i = 0; i <4; i++) { 
            curentUnit = _units[i] 
            if(_strLen(val)<5)
                break; 
            val = val / _dividend; 
            _str = val.toFixed(2)+curentUnit;
        }
    
        return _str;
    };

    public dealNum(val: number, flag: string = ''): string {
        let _str = `${val}`;
        let abv = Math.abs(val);
        if (abv >= 1000) {
            _str = `${val > 0 ? '' : '-'}${(abv / 1000).toFixed(2)}${flag}`;
        }
        return _str;
    }

    public formatNum(val: number, flag: string = ','): string {
        if (val < 1e3)
            return `${val}`;

        let _str = `${val}`;
        let _arr = []
        for (let i = 0; i < _str.length; i++) {
            (i > 0 && i % 3 == 0) && _arr.push(flag);
            _arr.push(_str[_str.length - 1 - i])
        }
        _arr.reverse();

        return _arr.join('');
    }

    public formatScore(val:number):string{
        if (val < 10000) return ''+val;
        let _str = '';
        let _units = ['万', '亿', '万亿'];
        let _dividend = 10000; 
        let curentUnit = _units[0]; //转换单位 
        let _strLen = (tempNum):number=>{ 
            let _strNum = tempNum.toString(); 
            let _index = _strNum.indexOf("."); 
            let _newNum = _strNum; 
            if(_index!=-1) _newNum = _strNum.substring(0,_index); 
            return _newNum.length
        }

        for (var i = 0; i <4; i++) { 
            curentUnit = _units[i] 
            if(_strLen(val)<5)
                break; 
            val = val / _dividend; 
            _str = val.toFixed(2)+curentUnit;
        }

        return _str;
    }

    // 查找数组中重复元素及出现个数
    public findRepeat(arrays:any):any{
        if (typeof(arrays) != 'object')
            return null;
        
            let _res = []; // 
        arrays.sort();
        for (let i = 0; i < arrays.length;) {
            let _count = 0;
            for (let j = i; j < arrays.length; j++) {
                if (arrays[i] == arrays[j])
                _count++;
            }
            _res.push([arrays[i], _count]);
            i += _count;
        }

        //_res 二维数维中保存了 值和值的重复数
        let _newArr = [];
        for (let i = 0; i < _res.length; i++)
            _newArr.push({val:_res[i][0],count:_res[i][1]});

        return _newArr;
    }

    /**
     * 数字前面补0
     *
     * @param {number} num 操作数字
     * @param {number} len 位数
     * @returns
     * @memberof UtilFun
     */
    public PrefixZero(num:number, len:number):string {
        return (Array(len).join('0') + num).slice(-len);
    }

    public getDataType(o:any):string{
        if (o===null) 
            return 'Null';
        else if (o===undefined) 
            return 'Undefined';
        else
            return Object.prototype.toString.call(o).slice(8,-1);
    };

    public deepCpy(source:Object):any{
        if (!this.isValid(source))
            return null;

        let _type = this.getDataType(source);
        let _result:any = null;
        if (_type === 'Array')
            _result = [];
        else if (_type === 'Object')
            _result = {};
        else
            return source;

        for (let k in source){
            let _tmp:any = source[k];
            _type = this.getDataType(_tmp);
            
            if (_type == 'Array' || _type == 'Object')
                _result[k]=arguments.callee(_tmp);//递归调用
            else
                _result[k]=_tmp;
        }
        return _result;
    };
    
    
    // 将source合并到des的尾部，返回合并后结果
    public mergeObject(des:Object,source:Object|Array<Object>):Object{
        if (!this.isValid(source) || (Array.isArray(source) && source.length < 1)) return null;
        if (!this.isValid(des)) des = {};

        let keys = Object.keys(source);
        for (let key of keys){
            if (Array.isArray(source[key])){
                des[key] = [];
                for (let it of source[key]){
                    des[key].push(it);
                }
            } else des[key] = source[key];
        }
        return des;
    };
    
    // 将source从des中移除
    public removeObject(des:Object,source:Object|Array<Object>):Object{
        if (!this.isValid(des)) return null;
        if (!this.isValid(source) || (Array.isArray(source) && source.length < 1)) return des;

        if (Array.isArray(source)){
            source.forEach((it:Object)=>{
                if (!this.isValid(it) || !(it instanceof Object)) return des;
                for (let key in it){
                    if(it.hasOwnProperty(key) && des.hasOwnProperty(key))
                    delete des[key];
                }
            });
        } else {
            for (let k in source)
                des.hasOwnProperty(k) && delete des[k];
        }
    };

    // obj中val是唯一的
    public getKeyByValue<T>(obj:Object,val:T):string{
        if (!this.isValid(obj) || !(obj instanceof Object)) return '';

        for (let key in obj){
            if (obj.hasOwnProperty(key) && obj[key] === val)
                return key;
        }

        return '';
    };

    // 将string转换为number\boolean\time（'2019-1-1 00:00:00'=>1546272000000）三种类型值
    public str2Value(str:string):any{
        if (this.isEmpty(str)) return '';

        if (typeof str !== 'string') return str;
        // boolean
        let _tmp = str.toLowerCase();
        if (_tmp === 'true' || _tmp === 'false') return _tmp === 'true';
        // 数字
        let _reg = new RegExp("^[0-9]*$");
        if(_reg.test(str)) return parseInt(str);
        // 日期
        let _time =  (new Date(str.replace(/-/g,'/'))).getTime();
        if (!isNaN(_time)) return _time;
        return str;
    };

    public obj2Json(obj:Object):any{
        if (!this.isValid(obj)) return null;
        if (typeof obj === 'string') return JSON.parse(obj);

        let _res = {};
        for (let k in obj){
            let val = obj[k];
            _res[k] = (typeof val === 'object'? this.obj2Json(val): this.str2Value(val));
        }

        return _res;
    };

    /**
     * 随机从数组中取一个元素
     * @param arr 数组
     */
    public randomArray<T>(arr:Array<T>):T{
        if (!arr || arr.length < 1)
            return null;

        return arr[Math.floor(Math.random()*arr.length)];;
    };

    /**
     * 将dec合并到src并返回合并后的数组
     * @param src 
     * @param dec 
     * @returns 
     */
    public concatArray<T>(src:T[],dec:T[]):T[]{
        !src && (src = []);
        if (!dec || dec.length < 1) return src;
        src = src.concat(dec).slice(0);
        return src;
    }

    /**
     * 是否事远程url,http https
     * @param url 
     */
    public isRemoteUrl(url: string):boolean{
        url = this.trim(url);
        return (url[4] === ':' || url[5] === ':');
    };

    // 通过url获取端口
    public getPortByUrl(url:string):number{
        if (!this.isEmpty(url)){
            let reg = new RegExp(":\\d+");
            let result = reg.exec(url);
            if (result) 
                return parseInt(result[0].slice(1));
        }
        return -1;
    };

    // 获取域名
    public getDomain(url: string): string{
        if (!this.isEmpty(url)) {
            let result = new RegExp("/[\\d\\w-_\\.]+").exec(url);
            if (result) {
                return result[0].slice(1);
            }
        }
        return '';
    };

    // 获取域名端口号
    public getDomainPort(url:string):number{
        if (!this.isEmpty(url)) {
            let result = new RegExp("/[\\d\\w-_\\.]+:\\d+").exec(url);
            if (result) {
                return parseInt(result[0].slice(1));
            }
        }
        return -1;
    };

    /**
     * 将日期字符串转化为时间
     * @param strDaste xxxx-xx-xx xx:xx:xx
     * return number
     */
    public getTimerByStr(strDate:string):number{
        let _timer:any = !this.isValid(strDate)?0:strDate;
        _timer = (new Date(strDate.replace(/-/g,'/'))).getTime();
        return _timer;
    };

    /**
     * 将时间戳格式化为yyyy-MM-dd HH:mm:ss
     * @param val 时间戳
     * @returns 
     */
    public formatTimeStamp(val:number):string{
        const date = new Date(val);
        const year = date.getFullYear();
        const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
        const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
        const hour = date.getHours()< 10 ? '0' + date.getHours() : date.getHours();
        const minute = date.getMinutes()< 10 ? '0' + date.getMinutes() : date.getMinutes();
        const second = date.getSeconds()< 10 ? '0' + date.getSeconds() : date.getSeconds();
        return `${year}-${month}-${day}  ${hour}:${minute}:${second}`; 
    }

    /**
     * 获取时间间隔
     * @param time 开始计时的时间点 (new Date()).getTime()
     * @returns 过去了多少秒
     */
    getTimeInterval(time:number):number{
        let _now = (new Date()).getTime();
        if (isNaN(time) || time < 0 || time > _now) {
            time = _now;
            return 0;
        }
        let _tmp = time;
        time = _now;
        return parseFloat(((_now-_tmp)/1000).toFixed(3));
    };

    /**
     * 随机一个整数 包括min和max
     */
    public random(min:number, max:number):number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        let count = Math.max(max - min, 0) + 1;
        return Math.floor(Math.random() * count) + min;
    }

    public createUUID():string{
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c)=>{
            let r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

}